<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    td{width: 10px;height: 10px;}
  </style>
</head>
<body>
  <table border="1"></table>
</body>
<script>

  // 生成地图类：40行80列的结构，提供边界值
  class Map{
    constructor(){
      this.cont = document.querySelector("table");
      // 初始行
      this.row = 15;
      // 初始列
      this.col = 30;
      // 准备存放每个单元格元素
      this.map = [];
      // 创建地图结构
      this.create();
    }
    create(){
      // 根据行
      for(let i=0;i<this.row;i++){
        // 创建tr
        const tr = document.createElement("tr");
        this.map.push([]);
        // 根据列
        for(let j=0;j<this.col;j++){
          // 创建td
          const td = document.createElement("td")
          this.map[i].push(td);
          tr.appendChild(td);
        }
        this.cont.appendChild(tr);
      }
    }
  }

  // 生成食物类：一个元素占据一个格子，随机生成位置，同一时间只能有一个食物出现
  class Food{
    constructor(){
      // 初始行和列的坐标
      this.x = random(0, m.col-1);    // 列
      this.y = random(0, m.row-1);    // 行
      // 设置元素样式
      this.randomPos()
    }
    create(){
      m.map[this.y][this.x].style.background = randomColor();
    }
    randomPos(){
      // 随机生成新的食物位置
      // 先清除之前的食物位置
      m.map[this.y][this.x].style.background = "#fff";
      // 随机位置
      this.x = random(0, m.col-1);    // 列
      this.y = random(0, m.row-1);    // 行
      // 设置新位置的元素样式
      this.create();
    }
  }

  // 生成蛇类：由多个连续的元素组成，有头有尾，向前持续移动，过程中可以使用键盘改变移动方向，移动过程中，头部的位置如何和食物重叠（吃食物），在尾部增加一个蛇节，移动过程中，头部的位置超出地图范围（撞墙）死，移动过程中，头部的位置和其他蛇节的位置重叠（吃到自己）死

  class snake{
    constructor(){
      // 初始蛇节信息：坐标和颜色
      this.body = [{x:5,y:3,color:"red"},{x:4,y:3,color:randomColor()},{x:3,y:3,color:randomColor()}];
      // 初始方向：r右，l左，t上，b下
      this.d = "r";
      // 创建蛇节
      this.create();
    }
    create(){
      // 根据蛇节信息，逐个设置蛇节位置的样式
      this.body.forEach(val=>{
        m.map[val.y][val.x].style.background = val.color;
      })
      // 延迟之后，开始移动
      setTimeout(()=>{
        this.move();
      }, 500);
    }
    move(){
      // 清除移动之前的蛇尾
      const x = this.body[this.body.length-1].x;
      const y = this.body[this.body.length-1].y;
      m.map[y][x].style.background = '#fff';
      // 从蛇尾依次向前开始移动
      for(let i=this.body.length-1;i>0;i--){
        this.body[i].x = this.body[i-1].x;
        this.body[i].y = this.body[i-1].y;
      }
      // 根据方向，控制蛇头的位置
      switch(this.d){
        case "l": this.body[0].x -= 1; break;
        case "r": this.body[0].x += 1; break;
        case "t": this.body[0].y -= 1; break;
        case "b": this.body[0].y += 1; break;
      }
      // 判断是否吃到食物
      this.eat();
      // 判断是否撞墙（蛇头的坐标超出了地图边界）
      if(this.body[0].x < 0 || this.body[0].y < 0 || this.body[0].x > m.col-1 || this.body[0].y > m.row-1){
        alert("撞墙了，over");
        return;
      }
      // 判断是否吃到自身（蛇头的坐标和任意一个其他蛇节的坐标重复）
      for(let i=1;i<this.body.length;i++){
        if(this.body[0].x === this.body[i].x && this.body[0].y === this.body[i].y){
          alert("撞自己了，over");
          return;
        }
      }
      // 以上只是在操作蛇节的坐标信息
      // 重新根据蛇节信息，生成蛇节位置的样式
      this.create();
    }
    eat(){
      // 判断蛇头是否和食物的坐标重复
      if(this.body[0].x === f.x && this.body[0].y === f.y){
        // 刷新食物位置
        f.randomPos();
        // 给蛇节，添加一个新对象
        this.body.push({
          x: this.body[this.body.length-1].x,
          y: this.body[this.body.length-1].y,
          color: randomColor()
        })
      }
    }
    deration(d){
      // 用来接收外部控制传入的方向
      // 不允许反方向移动
      switch(this.d){
        case "l":
          if(d==="r") return;
          break;
        case "t":
          if(d==="b") return;
          break;
        case "r":
          if(d==="l") return;
          break;
        case "b":
          if(d==="t") return;
          break;
      }
      // 如果不是反方向移动，保存本次新方向
      this.d = d;
    }
  }

  // 先创建地图
  const m = new Map();
  // 再创建食物
  const f = new Food();
  // 最后创建蛇
  const s = new snake();

  // 键盘控制方向
  document.onkeydown = function(eve){
    const e = eve || window.event;
    const keyCode = e.keyCode || e.which;
    switch(keyCode){
      // 将方向传入蛇对象的指定方法内
      case 37:s.deration("l");break;
      case 38:s.deration("t");break;
      case 39:s.deration("r");break;
      case 40:s.deration("b");break;
    }
  }

  // 测试用
  // setInterval(()=>{
  //   f.randomPos();
  // },1000)

  // 思考：
    // 1. 关卡（控制速度）
    // 2. 积分
    // 3. 开始和暂停和结束
    // 4. 食物种类
    // 5. 思考：如果食物随机到某个蛇节坐标，该如何处理
    // 6. 思考：快速切换反向方位置，报错
    // 7. 排名

  function random(a,b){
    return Math.round(Math.random()*(a-b)+b)
  }
  function randomColor(){
    return `rgb(${random(0,200)},${random(0,200)},${random(0,200)})`;
  }

</script>
</html>